/*实现异步日志缓冲区
    异步日志缓冲区思路：
        设计双缓冲区，业务线程向该缓冲区写入，写满之后，和另一个缓冲区进行交换
        由转门的线程去处理实际的写入过程
    交换时不能为空
    双缓冲区的设计概念比单缓冲区要节省更多的锁竞争带来的性能损耗
    在将日志信息写入到缓冲区的时候，写入的是日志格式化之后的字符串，并不构建完LogMsg之后再进行写入，这样就降低了很多构建时所需要的性能

*/
#ifndef __BUFFER__
#define __BUFFER__
#include "util.hpp"
#include <vector>
#include <assert.h>

namespace wu
{
    #define DEFAULT_BUFFER_SIZE (10*1024*1024)//10M//缓冲区的默认大小
    #define THRESHOLD_BUFFER_SIZE (80*1024*1024)//缓冲区阈值大小
    #define INCREMENT_BUFFER_SIZE (10*1024*1024)//缓冲区线性增长大小
    class Buffer
    {
    public:
        Buffer():_buffer(DEFAULT_BUFFER_SIZE), _writer_idx(0), _read_idx(0) {}
        //向缓冲区中写入数据
        void push(const char* data, size_t len)
        {
            //缓冲区的大小不够写入时1.直接返回阻塞 2.扩容
                //1. 实际应用方案
            //if(len > writeOfSize()) return;
                //2.进行扩容---极限性能测试方案
            ensureEnoughSize(len);
            //1.将数据拷贝到缓冲区
            std::copy(data, data+len, &_buffer[_writer_idx]);//起始位置,结束位置,拷贝目标位置
            //2.再偏移写指针位置
            moveWriter(len);
        }
        //可读数据起始位置
        const char* begin() { return &_buffer[_read_idx]; }
        //返回缓冲区的读写大小
        size_t readOfSize() { return (_writer_idx - _read_idx); }
        size_t writeOfSize()
        {
            //对于扩容方案来说这个接口是无意义的
            //这个接口仅对固定大小缓冲区提供
            return (_buffer.size() - _writer_idx);
        }
        //移动读位置
        void moveRead(size_t len)
        {
            //移动读位置指针的时候，一定要先看移动的位置是否合法,读指针也一样
            assert(len <= readOfSize());
            _read_idx += len;
        }
        //交换缓冲区的操作
        //重置读写位置，初始化缓冲区
        void reset()
        {
            _read_idx = 0;
            _writer_idx = 0;
            //读写位置相同表示没有数据可读,空
        }
        //交换两个缓冲区
        void swap(Buffer &buffer)
        {
            _buffer.swap(buffer._buffer);
            std::swap(_writer_idx, buffer._writer_idx);
            std::swap(_read_idx, buffer._read_idx);
        }
        bool empty() { return _read_idx == _writer_idx; }
    private:
        //移动写指针位置
        void moveWriter(size_t len)
        {
            assert(_writer_idx + len <= _buffer.size());
            _writer_idx += len;
        }
        void ensureEnoughSize(size_t len)
        {
            if(len <= writeOfSize()) return;//不需要进行扩容
            //扩容方案：没达到阈值时2倍增长 达到阈值之后线性增长
            size_t new_size = 0;
            if(_buffer.size() < THRESHOLD_BUFFER_SIZE) new_size = 2 * _buffer.size() + len;
            else new_size = _buffer.size() + INCREMENT_BUFFER_SIZE + len;

            _buffer.resize(new_size);
        }
    private:
        std::vector<char> _buffer;//不选择使用链表类的STL容器，因为结点的增加和释放也会带来一定的性能损耗
        size_t _writer_idx;//缓冲区的写位置
        size_t _read_idx;//缓冲区的读位置
    };
}
#endif